package top.ffhome.system.controller;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import eu.bitwalker.useragentutils.UserAgent;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import top.ffhome.common.config.BaseCurrentUser;
import top.ffhome.common.result.Result;
import top.ffhome.common.utils.IOUtils;
import top.ffhome.common.utils.IpUtils;
import top.ffhome.system.config.shiro.CurrentUser;
import top.ffhome.system.config.shiro.ShiroUtil;
import top.ffhome.system.entity.SysLoginLog;
import top.ffhome.system.entity.SysUser;
import top.ffhome.system.service.SysConfigService;
import top.ffhome.system.service.SysDictTypeService;
import top.ffhome.system.service.SysLoginLogService;
import top.ffhome.system.service.SysUserService;
import top.ffhome.system.vo.LoginBean;
import top.ffhome.util.bean.BeanUtil;

@CrossOrigin
@RestController
@RequestMapping("api")
@Api(tags = "登录控制器")
public class SysLoginController {
	@Autowired
	private Producer producer;
	@Autowired
	private CurrentUser currentUser;
	@Autowired
	private SysUserService userService;
	@Autowired
	private SysLoginLogService loginLogService;
	@Autowired
	private SysDictTypeService dictTypeService;
	@Autowired
	private SysConfigService configService;

	@GetMapping("/captcha.jpg")
	@ApiOperation("获取验证码图片")
	public void captcha() throws ServletException, IOException {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletResponse response = attributes.getResponse();
		HttpSession session = attributes.getRequest().getSession();
		response.setHeader("Cache-Control", "no-store, no-cache");
		response.setContentType("image/jpeg");

		// 生成文字验证码
		String text = producer.createText();
		// 生成图片验证码
		BufferedImage image = producer.createImage(text);
		// 保存到验证码到 session
		session.setAttribute(Constants.KAPTCHA_SESSION_KEY, text);

		ServletOutputStream out = response.getOutputStream();
		ImageIO.write(image, "jpg", out);
		IOUtils.closeQuietly(out);
	}

	@PostMapping("/login")
	@ApiOperation("用户登录")
	public Result login(@RequestBody LoginBean loginBean) {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		HttpSession session = request.getSession();
		// 从session中获取之前保存的验证码跟前台传来的验证码进行匹配
		Object kaptcha = session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
		if (kaptcha == null) {
			return Result.fail(-2, "验证码已失效");
		}
		if (loginBean.getCaptcha() == null || !loginBean.getCaptcha().equals(kaptcha)) {
			return Result.fail(-2, "验证码不正确");
		}

		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginBean.getAccount(),
				loginBean.getPassword());
		try {
			subject.login(usernamePasswordToken);
			if (subject.isAuthenticated()) {
				SysUser user = userService.selectByAccount(loginBean.getAccount());
				BeanUtil.constraintProperties(user, CollUtil.newArrayList("id", "account", "name"));
				ShiroUtil.setAttribute(BaseCurrentUser.CURRENT_USER, user);
				Map<String, Object> map = CollUtil.newHashMap(3);
				map.put("user", user);
				map.put("routers", userService.selectRoutersByUserId(user.getId()));
				map.put("menus", userService.selectMenusByUserId(user.getId()));
				map.put("perms", userService.selectPermsByUserId(user.getId()));
				UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
				loginLogService.insert(new SysLoginLog(user.getId(), IpUtils.getIp(), userAgent.getBrowser().getName(),
						userAgent.getOperatingSystem().getName()));
				return Result.success(map);
			}
		} catch (UnknownAccountException e) {
			return Result.fail(-3, "账号密码错误");
		} catch (IncorrectCredentialsException e) {
			return Result.fail(-3, "账号密码错误");
		}
		return Result.fail("未知的错误，请与管理员联系");
	}

	@GetMapping("/logout")
	@ApiOperation("用户登出")
	public Result logout() {
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		return Result.success("成功登出");
	}

	@PostMapping("/modifyPassword")
	@ApiOperation("修改自己的密码")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "id", value = "当前用户ID", dataType = "Long", paramType = "body", required = true),
			@ApiImplicitParam(name = "password", value = "原密码", dataType = "String", paramType = "body", required = true),
			@ApiImplicitParam(name = "newPassword", value = "新密码", dataType = "String", paramType = "body", required = true) })
	public Result modifyPassword(@RequestBody Map<String, String> params) {
		Long id = currentUser.getId();
		if (!id.toString().equals(params.get("id"))) {
			return Result.fail("非法的提交");
		}
		SysUser user = userService.get(id);
		if (!userService.encrypt(params.get("password"), user.getSalt()).equals(user.getPassword())) {
			return Result.fail("原密码不正确");
		}
		SysUser u = new SysUser();
		u.setId(id);
		u.setPassword(params.get("newPassword"));
		userService.update(u);

		return Result.success();
	}

	@GetMapping("/getConfig")
	@ApiOperation("获取配置信息")
	public Result getConfig() {
		Map<String, Object> map = MapUtil.newHashMap();
		map.put("typeList", dictTypeService.selectAll());
		map.put("config", configService.selectAll());
		return Result.success(map);
	}
}
